package com.tangyh.basic.util;
import com.tangyh.basic.utils.CommonUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
* 远程接口调用
*/
public class HttpUtils {

    /**
     * 60秒
     */
    private static final int HTTP_REQUEST_TIME_OUT = 60000;
    private static final int MAX_TOTAL = 50;
    private static final int MAX_CONN_PER_ROUTE = 20;

    private static final X509TrustManager trustManager = new X509TrustManager() {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] xcs, String str) {

        }

        @Override
        public void checkServerTrusted(X509Certificate[] xcs, String str) {

        }
    };

    /**
     * get
     *
     * @param headers
     * @param querys
     * @return
     * @throws Exception
     */
    public static HttpResponse doGet(String url,  Map<String, String> headers, Map<String, Object> querys) throws Exception {
        HttpClient httpClient = createHttpClient();

        HttpGet request = new HttpGet(buildUrl(url, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), String.valueOf(e.getValue()));
        }

        return httpClient.execute(request);
    }

    /**
     * Post String
     *
     * @param headers
     * @param querys
     * @param body
     * @return
     * @throws Exception
     */
    public static HttpResponse doPost(String url,
                                      Map<String, String> headers,
                                      Map<String, Object> querys,
                                      String body) throws Exception {
        HttpClient httpClient = createHttpClient();

        HttpPost request = new HttpPost(buildUrl(url, querys));
        for (Map.Entry<String, String> e : headers.entrySet()) {
            request.addHeader(e.getKey(), e.getValue());
        }

        if (!CommonUtil.isEmpty(body)) {
            request.setEntity(new StringEntity(body, "utf-8"));
        }

        return httpClient.execute(request);
    }

    private static String buildUrl(String host, Map<String, Object> querys) throws UnsupportedEncodingException {
        StringBuilder sbUrl = new StringBuilder();
        sbUrl.append(host);
        if (null != querys) {
            StringBuilder sbQuery = new StringBuilder();
            for (Map.Entry<String, Object> query : querys.entrySet()) {
                if (0 < sbQuery.length()) {
                    sbQuery.append("&");
                }
                if (CommonUtil.isEmpty(query.getKey()) && !CommonUtil.isEmpty(query.getValue())) {
                    sbQuery.append(query.getValue());
                }
                if (!CommonUtil.isEmpty(query.getKey())) {
                    sbQuery.append(query.getKey());
                    if (!CommonUtil.isEmpty(query.getValue())) {
                        sbQuery.append("=");
                        sbQuery.append(URLEncoder.encode(String.valueOf(query.getValue()), "utf-8"));
                    }
                }
            }
            if (0 < sbQuery.length()) {
                sbUrl.append("?").append(sbQuery);
            }
        }

        return sbUrl.toString();
    }

    /**
     * 创建HttpClient，支持http和https请求方式
     * @return 返回示例对象
     */
    private static HttpClient createHttpClient() {
        HttpClient httpClient = HttpClients.createDefault();
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            // 初始化SSL上下文
            sslContext.init(null, new TrustManager[]{trustManager}, null);
            // SSL套接字连接工厂,NoopHostnameVerifier为信任所有服务器
            SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            // 注册http套接字工厂和https套接字工厂
            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", sslFactory)
                    .build();
            // 连接池管理器
            PoolingHttpClientConnectionManager pcm = new PoolingHttpClientConnectionManager(registry);
            //连接池最大连接数
            pcm.setMaxTotal(MAX_TOTAL);
            //每个路由最大连接数
            pcm.setDefaultMaxPerRoute(MAX_CONN_PER_ROUTE);
            /*
             *  请求参数配置
             *  connectionRequestTimeout:
             *           从连接池中获取连接的超时时间，超过该时间未拿到可用连接，
             *           会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
             *  connectTimeout:
             *           连接上服务器(握手成功)的时间，超出该时间抛出connect timeout
             *  socketTimeout:
             *           服务器返回数据(response)的时间，超过该时间抛出read timeout
             */
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectionRequestTimeout(HTTP_REQUEST_TIME_OUT)
                    .setConnectTimeout(HTTP_REQUEST_TIME_OUT)
                    .setSocketTimeout(HTTP_REQUEST_TIME_OUT)
                    .build();

            httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .setConnectionManager(pcm)
                    .build();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return httpClient;
    }
}
